#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "2d5p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"

__thread_local volatile unsigned long get_reply,put_reply;


__thread_local double A_lhs[BLKX][BLKY];
__thread_local double A_rhs[BLKX+2][BLKY+2];
//Record the logical information of this time executed task
__thread_local int dest,src,i,j,s_iter,s_starti,s_endi,s_startj,s_endj;
//Record the logical information of the last time executed task.
__thread_local int last_iter,last_starti,last_startj;
__thread_local int tsize, bsize, stride;   



extern double A[2][SIZEX][SIZEY];

#define alpha_d 0.0876
#define beta_d  0.0765
#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start1,dma_end1,dma_start2,dma_end2,dma_start3,dma_end3;
__thread_local volatile unsigned long comp_start,comp_end;
__thread_local int core_id=0;
extern unsigned long slave_comp_count[64];
extern unsigned long slave_dma_count[64];
extern unsigned long slave_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

void stencil_core(targs* args)
{
   //long  array_size;
   int i,j,ii,jj;
   int dest,src,s_iter,s_starti,s_endi,s_startj,s_endj;
   int core_id;
   core_id = athread_get_id(-1);
   s_iter = args->iter;
   dest = (s_iter) % 2 ; 
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
#ifdef _PROFILING
   start=rtc_();
#endif
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj)
   {
     int i,j;
#ifdef _PROFILING
   dma_start1=rtc_();
#endif
      //core data (lhs->rhs)
      for(i = 0; i < BLKX; i++)
       for(j = 0; j < BLKY; j++)
         A_rhs[i+1][j+1] = A_lhs[i][j];
      get_reply = 0;
      //1.1 update up and down shade data
      athread_get(PE_MODE,&A[src][s_starti-1][s_startj],&A_rhs[0][1],BLKY*8,(void*)&get_reply,0,0,0); 
      athread_get(PE_MODE,&A[src][s_endi][s_startj], &A_rhs[BLKX+1][1],BLKY*8,(void*)&get_reply,0,0,0);
      //1.2 left right shade data (mmem->lhs)
      stride = (SIZEY-1)*8;
      bsize = 8;
      tsize = BLKX*8;
      athread_get(PE_MODE,&A[src][s_starti][s_startj-1]
                     , &A_lhs[0][0]
                     ,tsize,(void*)&get_reply,0,stride,bsize);
      athread_get(PE_MODE,&A[src][s_starti][s_endj]
                     , &A_lhs[1][0]
                     ,tsize,(void*)&get_reply,0,stride,bsize); 
      while(get_reply != 4);
      //left right shade data (lhs->rhs)
      for(i=0;i<BLKX;++i)
      {  A_rhs[i+1][BLKY+1]=A_lhs[1][i];
         A_rhs[i+1][0]=A_lhs[0][i];
      }
#ifdef _PROFILING
   dma_end1=rtc_();
   slave_dma_count[core_id]+=((dma_end1-dma_start1));   
   dma_end1=dma_start1=0;
#endif
   }else{
     int bsize,tsize,stride;
#ifdef _PROFILING
  dma_start2=rtc_();
#endif
     // 1.fetch data
     get_reply=0;  
     bsize=(s_endj-s_startj+2)*8;
     stride=(SIZEY-(s_endj-s_startj+2))*8;  //stride: from blocktail to blockhead 
     tsize= bsize*(s_endi-s_starti+2);
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj-1]
                 , &A_rhs[0][0]
                 , tsize,(void*)&get_reply
                 , 0, stride, bsize);
     //2.synchronize, wait for the transfer finish
     while(get_reply!=1);
#ifdef _PROFILING
  dma_end2=rtc_();
  slave_dma_count[core_id]+=((dma_end2-dma_start2)); 
  dma_end2=dma_start2=0;
#endif
     last_iter = s_iter;
     last_starti = s_starti;
     last_startj = s_startj;   
  }
#ifdef _PROFILING
  comp_start=rtc_();
#endif
   //comp
/*  for(ii = 1; ii < BLKX+1; ++ii)
     for(jj = 1; jj < BLKY+1; ++jj)
        A_lhs[ii-1][jj-1] = alpha_d * A_rhs[ii][jj] + \
                     beta_d * (A_rhs[ii-1][jj] + A_rhs[ii+1][jj] +\
                     A_rhs[ii][jj-1] + A_rhs[ii][jj+1]);*/
    for(ii = 1; ii < BLKX+1; ++ii)
      for(jj = 1; jj < BLKY-BLKY%6; jj+=6)
      {
         A_lhs[ii-1][jj-1] = alpha_d * (A_rhs[ii][jj]) + \
                     beta_d * (A_rhs[ii-1][jj] + A_rhs[ii+1][jj] +\
                     A_rhs[ii][jj-1] + A_rhs[ii][jj+1]);
         A_lhs[ii-1][jj] = alpha_d * (A_rhs[ii][jj+1]) + \
                     beta_d * (A_rhs[ii-1][jj+1] + A_rhs[ii+1][jj+1] +\
                     A_rhs[ii][jj] + A_rhs[ii][jj+2]);
         A_lhs[ii-1][jj+1] = alpha_d * (A_rhs[ii][jj+2]) + \
                     beta_d * (A_rhs[ii-1][jj+2] + A_rhs[ii+1][jj+2] +\
                     A_rhs[ii][jj+1] + A_rhs[ii][jj+3]);
         A_lhs[ii-1][jj+2] = alpha_d * (A_rhs[ii][jj+3]) + \
                     beta_d * (A_rhs[ii-1][jj+3] + A_rhs[ii+1][jj+3] +\
                     A_rhs[ii][jj+2] + A_rhs[ii][jj+4]);
         A_lhs[ii-1][jj+3] = alpha_d * (A_rhs[ii][jj+4]) + \
                     beta_d * (A_rhs[ii-1][jj+4] + A_rhs[ii+1][jj+4] +\
                     A_rhs[ii][jj+3] + A_rhs[ii][jj+5]);
         A_lhs[ii-1][jj+4] = alpha_d * (A_rhs[ii][jj+5]) + \
                     beta_d * (A_rhs[ii-1][jj+5] + A_rhs[ii+1][jj+5] +\
                     A_rhs[ii][jj+4] + A_rhs[ii][jj+6]);
      }
    for(ii = 1; ii < BLKX+1; ++ii)
      for(jj = BLKY-BLKY%6; jj<BLKY+1; ++jj)
         A_lhs[ii-1][jj-1] = alpha_d * (A_rhs[ii][jj]) + \
                     beta_d * (A_rhs[ii-1][jj] + A_rhs[ii+1][jj] +\
                     A_rhs[ii][jj-1] + A_rhs[ii][jj+1]);
   //write back
#ifdef _PROFILING
  comp_end=rtc_();
  slave_comp_count[core_id]+=(comp_end-comp_start);
  comp_end=comp_start=0;
  dma_start3=rtc_();
#endif
   put_reply=0;
   bsize=(BLKY)*8;
   stride=(SIZEY-(BLKY))*8;  //stride: from blocktail to blockhead 
   tsize= bsize*BLKX;
   athread_put(PE_MODE,&A_lhs[0][0]
                     , &A[dest][s_starti][s_startj]
                     ,tsize,(void*)&put_reply,stride,bsize);
   while(put_reply!=1);
#ifdef _PROFILING
    dma_end3=rtc_();
    slave_dma_count[core_id]+=((dma_end3-dma_start3));
    dma_end3=dma_start3=0;
    end=rtc_();
    //slave_count[core_id]+=(end-start);     
#endif
}
